/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.game.math.expression;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import nl.flotsam.xeger.Xeger;
import org.game.math.view.domain.MathExpression;
import javax.script.ScriptEngineManager;

/**
 *
 * @author nmduc
 */
public class GenerateExpression {

    private static GenerateExpression generateExpression;
    private static ScriptEngine scriptEngine;
    private static NumberFormat numberFormat = new DecimalFormat("####.##");

    private static ScriptEngine getScriptEngine() {
        if (scriptEngine == null) {
            ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
            scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
        }
        return scriptEngine;
    }

    public static GenerateExpression getInstance() {
        if (generateExpression == null) {
            generateExpression = new GenerateExpression();
        }
        return generateExpression;
    }

    protected String generateNumberGrammar(boolean signed, int length) {
        StringBuilder numberGrammar = new StringBuilder();

        if (signed) {
            numberGrammar.append("[\\-]{0,1}");
        }
        numberGrammar.append("[0-9]{1,").append(length).append("}");
        return numberGrammar.toString();
    }

    protected String generateOperatorGrammar(Set<Operator> operators) {
        StringBuilder operatorGrammar = new StringBuilder("[");
        for (Operator operator : operators) {
            if (operator == Operator.SUB) {
                operatorGrammar.append("\\").append(operator.getOperatorChar());
            } else {
                operatorGrammar.append(operator.getOperatorChar());
            }
        }
        operatorGrammar.append("]");
        return operatorGrammar.toString();
    }

    protected String generateExpressionGrammar(Set<Operator> operators, int numOperand, boolean signed, int length) {
        StringBuilder expressionGrammar = new StringBuilder();
        String numberGrammar = generateNumberGrammar(signed, length);
        String operatorGrammar = generateOperatorGrammar(operators);
        expressionGrammar.append(numberGrammar);
        for (int i = 0; i < numOperand; i++) {
            expressionGrammar.append(operatorGrammar).append(numberGrammar);
        }
        return expressionGrammar.toString();
    }

    public List<MathExpression> generateExpression(int expNum, Set<Operator> operators, int numOperand, boolean signed,
            int length) {
        List<MathExpression> expressions = new ArrayList<MathExpression>();

        String expressionGrammar = generateExpressionGrammar(operators, numOperand, signed, length);
        Xeger expressionGen = new Xeger(expressionGrammar);
        int i = 0;
        ScriptEngine se = GenerateExpression.getScriptEngine();
        while (i < expNum) {
            MathExpression mathExpression = new MathExpression();
            mathExpression.setExpression(expressionGen.generate());
            mathExpression.setInputResult("");
            try {
                Double val = (Double) se.eval(mathExpression.getExpression());
                if (!Double.isInfinite(val)) {
                    mathExpression.setResult(numberFormat.format(val));
                }

//                int sum = ().intValue();
//                int sum = parser.Input();
//                mathExpression.setResult(Integer.toString(val));
//            } catch (ParseException ex) {
//                Logger.getLogger(GenerateExpression.class.getName()).log(Level.SEVERE, "Expression = " + mathExpression.
//                        getExpression(), ex);
//            } catch (TokenMgrError ex) {
//                Logger.getLogger(GenerateExpression.class.getName()).log(Level.SEVERE, "Expression = " + mathExpression.
//                        getExpression(), ex);
            } catch (ScriptException ex) {
                Logger.getLogger(GenerateExpression.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ArithmeticException ex) {
                String message = "Expression = " + mathExpression.getExpression() + " div by 0.";
                Logger.getLogger(GenerateExpression.class.getName()).log(Level.SEVERE, message);
                continue;
            }
            expressions.add(mathExpression);
            i++;
        }
        return expressions;
    }

    public List<MathExpression> generateMultiplyTable(int multipliedNum) {
        List<MathExpression> expressions = new ArrayList<MathExpression>();
        int result = 0;
        for (int i = 1; i < 11; i++) {
            MathExpression mathExpression = new MathExpression();
            mathExpression.setExpression("" + multipliedNum + "x" + i);
            mathExpression.setInputResult("");
            result += multipliedNum;
            mathExpression.setResult(Integer.toString(result));
            expressions.add(mathExpression);
        }
        return expressions;
    }
}
